package cn.italkcloud.cph.service.impl;

import cn.italkcloud.cph.constants.ReportTimeRangeEnum;
import cn.italkcloud.cph.constants.enums.vos.FeeLogTypeEnum;
import cn.italkcloud.cph.entity.ReportVosCdr;
import cn.italkcloud.cph.entity.VosCustomer;
import cn.italkcloud.cph.entity.VosFeeLog;
import cn.italkcloud.cph.entity.VosServer;
import cn.italkcloud.cph.entity.dto.vos.CdrDTO;
import cn.italkcloud.cph.entity.dto.vos.CustomerDetailDTO;
import cn.italkcloud.cph.entity.dto.vos.PayHistoryDTO;
import cn.italkcloud.cph.entity.wrapper.VosWrapper;
import cn.italkcloud.cph.helper.VosHelper;
import cn.italkcloud.cph.service.*;
import cn.italkcloud.cph.utils.BaseUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author dominate
 * @since 2024/7/9
 **/
@Service
@AllArgsConstructor
@Slf4j
public class VosServiceImpl implements VosService {

    private final IVosCustomerService vosCustomerService;
    private final IVosFeeLogService vosFeeLogService;
    private final IVosServerService vosServerService;
    private final IReportVosCdrService reportVosCdrService;


    private static String createFeeKey(BigDecimal change, BigDecimal after) {
        return change + "_" + after;
    }

    @Override
    public void syncCustomerAndFee() {
        List<VosServer> serverList = vosServerService.getValid();
        for (VosServer server : serverList) {
            syncServerCustomerAndFee(server);
        }
    }

    private void syncServerCustomerAndFee(VosServer server) {
        List<VosCustomer> vosCustomerList = vosCustomerService.getValidList(server.getId());
        Map<String, VosCustomer> vosCustomerMap = new HashMap<>(vosCustomerList.size());
        for (VosCustomer vosCustomer : vosCustomerList) {
            vosCustomerMap.put(vosCustomer.getVosAccount(), vosCustomer);
        }
        long startTime = System.currentTimeMillis();
        log.info("Sync VOS , customer count {}", vosCustomerMap.size());
        List<CustomerDetailDTO> customerDetailList = VosHelper.getCustomerDetailList(server.getApiUrl(), vosCustomerMap.keySet().toArray(new String[0]));
        log.info("Sync VOS , get customer count {}", customerDetailList.size());
        List<VosCustomer> updateList = new ArrayList<>();
        List<VosFeeLog> addFeeLogList = new ArrayList<>();
        for (CustomerDetailDTO customerDetail : customerDetailList) {
            VosCustomer update = new VosCustomer();
            VosCustomer vosCustomer = vosCustomerMap.get(customerDetail.getAccount());
            // 相同名称和属性的字段对比
            if (BaseUtil.compare(vosCustomer, customerDetail, update)) {
                update.setId(vosCustomer.getId());
            }
            // 转换时间
            if (vosCustomer.getStartTime().getTime() != customerDetail.getStartTime()) {
                update.setStartTime(new Date(customerDetail.getStartTime())).setId(vosCustomer.getId());
            }
            if (vosCustomer.getValidTime().getTime() != customerDetail.getValidTime()) {
                update.setValidTime(new Date(customerDetail.getValidTime())).setId(vosCustomer.getId());
            }
            // 如果余额变化 需记录金额变化
            if (0 != vosCustomer.getMoney().compareTo(customerDetail.getMoney())) {
                update.setMoney(customerDetail.getMoney()).setId(vosCustomer.getId());
                BigDecimal change = customerDetail.getMoney().subtract(vosCustomer.getMoney());
                addFeeLogList.add(VosWrapper.builder().createFeeLog(FeeLogTypeEnum.CONSUME, change,
                        customerDetail.getMoney(), vosCustomer));
            }
            if (Objects.nonNull(update.getId())) {
                updateList.add(update);
            }
        }
        if (CollectionUtils.isNotEmpty(updateList)) {
            vosCustomerService.updateBatchById(updateList);
        }
        log.info("Sync VOS , update customer count {}", updateList.size());
        if (CollectionUtils.isNotEmpty(addFeeLogList)) {
            vosFeeLogService.saveBatch(addFeeLogList);
        }
        log.info("Sync VOS , add FeeLog count {}", addFeeLogList.size());
        log.info("Sync VOS finished use {}ms", System.currentTimeMillis() - startTime);
    }

    @Override
    public void syncRechargeFeeLog(Date startDate, Date endDate) {
        List<VosServer> serverList = vosServerService.getValid();
        for (VosServer server : serverList) {
            syncServerRechargeFeeLog(server, startDate, endDate);
        }
    }

    private void syncServerRechargeFeeLog(VosServer server, Date startDate, Date endDate) {
        List<VosCustomer> vosCustomerList = vosCustomerService.getValidList(server.getId());
        List<Integer> vosCustomerIdList = new ArrayList<>(vosCustomerList.size());
        for (VosCustomer vosCustomer : vosCustomerList) {
            vosCustomerIdList.add(vosCustomer.getId());
        }
        List<VosFeeLog> feeLogList = vosFeeLogService.loadRechargeLog(vosCustomerIdList, startDate, endDate);
        Map<String, VosFeeLog> feeLogMap = new HashMap<>();
        for (VosFeeLog feeLog : feeLogList) {
            String key = createFeeKey(feeLog.getChange(), feeLog.getBalance());
            feeLogMap.put(key, feeLog);
        }
        List<VosFeeLog> updateList = new ArrayList<>();
        for (VosCustomer vosCustomer : vosCustomerList) {
            List<PayHistoryDTO> pagLogList = VosHelper.getPayHistory(server.getApiUrl(), vosCustomer.getVosAccount(), startDate, endDate);
            for (PayHistoryDTO payLog : pagLogList) {
                String key = createFeeKey(payLog.getFee(), payLog.getCustomerMoney());
                if (!feeLogMap.containsKey(key)) {
                    continue;
                }
                VosFeeLog update = new VosFeeLog()
                        .setId(feeLogMap.get(key).getId())
                        .setComment(payLog.getMemo());
                updateList.add(update);
            }
        }
        if (CollectionUtils.isNotEmpty(updateList)) {
            vosFeeLogService.updateBatchById(updateList);
        }
    }

    @Override
    public boolean modifyCustomer(String apiUrl, VosCustomer target) {
        VosCustomer base = vosCustomerService.getById(target.getId());
        VosCustomer update = new VosCustomer();
        if (!BaseUtil.compare(base, target, update)) {
            return true;
        }
        update.setId(target.getId());
        if (vosCustomerService.updateById(update)) {
            return VosHelper.modifyCustomer(apiUrl, base.getVosAccount(), target.getVosName(),
                    target.getLimitMoney(), target.getLockType(), target.getValidTime());
        }
        return false;
    }

    @Override
    public void calculateReport(Date startDate, Date endDate, ReportTimeRangeEnum reportTimeRangeEnum) {
        List<VosServer> serverList = vosServerService.getValid();
        for (VosServer server : serverList) {
            calculateReport(server, startDate, endDate, reportTimeRangeEnum);
        }
    }

    @Override
    public void mergeReport(Date startDate, Date endDate) {
        List<VosServer> serverList = vosServerService.getValid();
        for (VosServer server : serverList) {
            reportVosCdrService.mergeReport(server.getId(), startDate, endDate);
        }
    }

    private void calculateReport(VosServer vosServer, Date startDate, Date endDate, ReportTimeRangeEnum reportTimeRangeEnum) {
        long startTime = System.currentTimeMillis();
        if (!VosHelper.checkServerMysqlConnection(vosServer.getMysqlUrl(), vosServer.getMysqlUser(), vosServer.getMysqlPassword())) {
            log.info("Calculate VOS Report , Server {} Mysql connection failed", vosServer.getName());
            return;
        }
        int latestVosCdrId = reportVosCdrService.getLatestVosCdrId(vosServer.getId(), startDate);
        log.info("Calculate VOS Report , Server {}  CDR Latest id {}", vosServer.getName(), latestVosCdrId);
        List<CdrDTO> cdrList = VosHelper.queryCdrReport(vosServer, startDate, endDate, latestVosCdrId);
        log.info("Calculate VOS Report , Server {} CDR List count {}", vosServer.getName(), cdrList.size());
        if (cdrList.isEmpty()) {
            return;
        }
        List<ReportVosCdr> cdrReportList = reportVosCdrService.analysisAfterSave(vosServer.getId(), reportTimeRangeEnum, cdrList);
        log.info("Calculate VOS Report , Server {} finished use {}ms", vosServer.getName(), System.currentTimeMillis() - startTime);

    }


}
